/* OpenTeddy 3d sketch modeler based on the paper by T. Igarashi et al.
 *      "Teddy: A Sketching Interface for 3D Freeform Design"
 *
 * Copyright (c) 2004 Alex Ni
 *
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* OpenTeddy 3d sketch modeler based on the paper by T. Igarashi et al.
*      "Teddy: A Sketching Interface for 3D Freeform Design"
*
* Copyright (c) 2004 Alex Ni
*/


#include "otconfig.h"

#include "GTS/gts.h"

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <string>

extern "C"
{
#include "triangle.h"
#include "trackball.h"
}

// C++
#include <map>
#include <list>
#include <deque>
#include <vector>

// Our includes
#include "main.h"
#include "pub_infl.h"

#include "../glfw/win32/glfw.h"

// We need to make sure OpenGL and GLib are 'united.'
static struct confirm_double_size
{
	confirm_double_size()
	{
		OT_ASSERT( sizeof(double) == sizeof(gdouble) );
	}
}
confirm_double_size_singleton;

// probably should find a better place to put this...
static double ot_2d_dist(double x1, double y1, double x2, double y2)
{
	double dx = (x1 - x2);
	double dy = (y1 - y2);
	return sqrt(dx*dx + dy*dy);
}

struct OT2vec
{
	double v[2];
	OT2vec(){}
	OT2vec(double x, double y)
	{
		v[0] = x; v[1] = y;
	}
	double& operator[](int i)
	{
		OT_ASSERT( (i>=0) && (i <= 2) );
		return v[i];
	}
	void operator+=(OT2vec& ov2)
	{
		v[0] += ov2[0]; v[1] += ov2[1];
	}
	void operator-=(OT2vec& ov2)
	{
		v[0] -= ov2[0]; v[1] -= ov2[1];
	}
	OT2vec operator-(OT2vec& ov2)
	{
		return OT2vec(v[0] - ov2[0], v[1] - ov2[1]);
	}
	double len()
	{
		static OT2vec zero(0.0, 0.0);
		return dist(zero);
	}

	double dist(OT2vec& ov2)
	{
		return ot_2d_dist(v[0], v[1], ov2[0], ov2[1]);
	}
	double cross(OT2vec& ov)
	{
		return v[0] * ov[1] - v[1] * ov[0];
	}
	double dot(OT2vec& ov)
	{
		return v[0] * ov[0] + v[1] * ov[1];
	}
	void scale(double d)
	{
		v[0] *= d; v[1] *= d;
	}
	void normalize()
	{
		double d = len();
		v[0] /= d; v[1] /= d;
	}
	OT2vec interpolate(OT2vec& ov, double perc)
	{
		return OT2vec( v[0] + (ov[0] - v[0])*perc, v[1] + (ov[1] - v[1])*perc );
	}
	OT2vec invslope()
	{
		double x[3], v1[3], v2[3];
		v1[0] = v[0]; v1[1] = v[1]; v1[2] = 0.0;
		v2[0] = v2[1] = 0.0; v2[2] = 1.0;
		gts_vector_cross(x, v1, v2);
		return OT2vec(x[0], x[1]);
	}
};


// assumes [x y z 1] input
void ot_gl_mat_mult_3(double* m, double* v, double* vr)
{
	vr[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * 1.0;
	vr[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * 1.0;
	vr[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * 1.0;
	vr[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * 1.0;
}

void ot_matrix_scale(double* m, double d)
{
	for (int i = 0;i < 16;i++)
	{
		m[i] *= d;
	}
}


double ot_matrix_determinant(double* mat)
{
	double m[4][4] ;

	for (int i = 0;i < 16;i++)
	{
		m[i % 4][i / 4] = mat[i];
	}

	return
		m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] +
		m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] - m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] +
		m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] - m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] +
		m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] - m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] +
		m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] - m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] +
		m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] - m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3];
}

void ot_matrix_transpose(double* r, double* mat)
{
	for (int i = 0;i < 16;i++)
	{
		r[ (i % 4)*4 + (i / 4) ] = mat[ (i / 4) * 4 + (i % 4) ];
	}
}

void ot_matrix_invert(double* r, double* mat)
{
	double m[4][4] ;
	for (int i = 0;i < 16;i++)
	{
		m[i % 4][i / 4] = mat[i];
	}

	r[0] = m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3];
	r[4] = m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3];
	r[8] = m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3];
	r[12] = m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3];
	r[1] = m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3];
	r[5] = m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3];
	r[9] = m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3];
	r[13] = m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3];
	r[2] = m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3];
	r[6] = m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3];
	r[10] = m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3];
	r[14] = m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3];
	r[3] = m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2];
	r[7] = m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2];
	r[11] = m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2];
	r[15] = m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2];
	ot_matrix_scale(r, 1.0 / (ot_matrix_determinant(mat)));
}



struct OT3vec
{
	double v[3];
	OT3vec(){; }
	OT3vec(double x, double y, double z)
	{
		v[0] = x; v[1] = y; v[2] = z;
	}

	void matmult(double* m)
	{
		double vr[4];
		ot_gl_mat_mult_3(m, v, vr);
		vr[3] = 1.0;
		v[0] = vr[0] / vr[3]; v[1] = vr[1] / vr[3]; v[2] = vr[2] / vr[3];
	}

	inline double& operator[](int i)
	{
		//OT_ASSERT( (i>=0) && (i<=3) );
		return v[i];
	}

	inline double operator[](int i) const
	{
		//OT_ASSERT( (i>=0) && (i<=3) );
		return v[i];
	}

	void operator+=(OT3vec& v2)
	{
		v[0] += v2[0]; v[1] += v2[1]; v[2] += v2[2];
	}
	void operator-=(OT3vec& v2)
	{
		v[0] -= v2[0]; v[1] -= v2[1]; v[2] -= v2[2];
	}
	void set_cross(OT3vec& v1, OT3vec& v2)
	{
		gts_vector_cross((*this), v1, v2);
	}
	inline double length()
	{
		return sqrt( (v[0]*v[0]) + (v[1]*v[1]) + (v[2]*v[2]) );
	}
	double dist(OT3vec& v1)
	{
		return OT3vec(v[0] - v1[0], v[1] - v1[1], v[2] - v1[2]).length();
	}
	double dot(OT3vec& v1)
	{
		return v[0]*v1[0] + v[1]*v1[1] + v[2]*v1[2];
	}
	void scale(double d)
	{
		v[0] *= d; v[1] *= d; v[2] *= d;
	}

	inline void normalize()
	{
		double len = length();
		v[0] /= len; v[1] /= len; v[2] /= len;
	}
};

typedef std::vector<OT2vec> OT2list;
class OT2loop : public OT2list
{
public:
	OT2vec& wrap(int i)
	{
		return operator[](i % size());
	}

	void operator-=(OT2loop& sub)
	{
		OT2list::iterator myi = begin();
		OT2list::iterator youri = sub.begin();
		for (; (myi != end()) && (youri != sub.end()); myi++, youri++)
		{
			(*myi) -= (*youri);
		}
	}
	OT2vec tangat(size_t i)
	{
		if (size() < 2) return OT2vec(0.0, 0.0);
		if (i >= (size() - 1)) i--;
		OT2vec va = (*this)[i];
		OT2vec vb = (*this)[i + 1];
		vb -= va;
		vb.normalize();
		return vb;
	}

	OT2vec normat(size_t i)
	{
		OT2vec tang = tangat(i);
		return tang.invslope();
	}
};

void ot_protected_strncpy(char* dest, char* src, int buffersize)
{
	strncpy(dest, src, buffersize);
	dest[buffersize - 1] = '\0';
}


///////////////////////////////////////////////////////////////////////////////////
// GTS class stuff
///////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////
// derive our own vertex class in order to include normal information
///////////////////////////////////////////////////////////////////////////////////
GtsVertexClass * ot_gts_vertex_class (void);

struct OTGtsVertex
{
	GtsVertex v;

	OT3vec n;
};

struct OTGtsVertexClass
{
	GtsVertexClass parent_class;

	void (* intersection_attributes) (GtsVertex *,
		GtsObject *,
		GtsObject *);
};

#define OT_GTS_VERTEX(v) ((OTGtsVertex*)(v))


static void ot_gts_vertex_destroy (GtsObject* object)
{
	(* GTS_OBJECT_CLASS (ot_gts_vertex_class ())->parent_class->destroy) (object);
}

static void ot_gts_vertex_clone (GtsObject * clone, GtsObject * object)
{
	(* GTS_OBJECT_CLASS (ot_gts_vertex_class ())->parent_class->clone) (clone, object);
	OT_GTS_VERTEX (clone)->n = OT_GTS_VERTEX (object)->n;
}

static void ot_gts_vertex_class_init (GtsVertexClass * klass)
{
	klass->intersection_attributes = NULL;
	GTS_OBJECT_CLASS (klass)->clone = ot_gts_vertex_clone;
	GTS_OBJECT_CLASS (klass)->destroy = ot_gts_vertex_destroy;
}

static void ot_gts_vertex_init (OTGtsVertex * vertex)
{
	vertex->n = OT3vec(0.0, 0.0, 0.0);
}

GtsVertexClass* ot_gts_vertex_class (void)
{
	static GtsVertexClass * klass = NULL;

	if (klass == NULL)
	{
		GtsObjectClassInfo vertex_info = {
			"OTGtsVertex",
			sizeof (OTGtsVertex),
			sizeof (OTGtsVertexClass),
			(GtsObjectClassInitFunc) ot_gts_vertex_class_init,
			(GtsObjectInitFunc) ot_gts_vertex_init,
			(GtsArgSetFunc) NULL,
			(GtsArgGetFunc) NULL
		};
		klass = (GtsVertexClass*)gts_object_class_new (GTS_OBJECT_CLASS (gts_vertex_class ()),
			&vertex_info);
	}

	return klass;
}
///////////////////////////////////////////////////////////////////////////////////
// end gts class stuff
///////////////////////////////////////////////////////////////////////////////////